<?php
namespace App\Console\Commands;

use App\Handlers\ChatSwooleHandler;
use Illuminate\Console\Command;

class ChatSwoole extends Command
{
    public $ws;
    protected $pid;
    protected $table;
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'chat_swoole:9504 {action?}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'swoole';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        $this->detectSwoole(); //检测是否加载了swoole扩展
        $action = $this->argument('action');
        switch ($action) {
            case 'start':
                $this->start();
                break;
            case 'stop':
                $this->stop();
                break;
            case 'reload':
                $this->reload();
                break;
            case 'restart':
                $this->info('swoole server restarted');
                $this->restart();
                break;
            case 'close':
                $this->info('swoole server closed');
                break;
            case 'infos':
                $this->infos();
                break;
            default:
                $this->error("Invalid argument '$action'. ");
                break;
        }
    }

    public function start()
    {
        if ($this->isRunning($this->getPid())) {
            $this->error('Failed! The swoole process is already running.');
            exit(1);
        }
        $this->info('swoole server started');
        //创建websocket服务器对象，监听0.0.0.0:9503端口
        $this->ws = new \swoole_websocket_server(config('swoole_websocket.settings.swoole.swoole9504.server.host'), config('swoole_websocket.settings.swoole.swoole9504.server.port'));
        //设置websocket配置值
        $this->ws->set(array(
            'worker_num' => config('swoole_websocket.settings.swoole.swoole9504.options.worker_num'),
            'daemonize' => config('swoole_websocket.settings.swoole.swoole9504.options.daemonize'),
            'log_file' => config('swoole_websocket.settings.swoole.swoole9504.options.log_file'),
            'pid_file' => $this->getPidPath(),
            'max_request' => config('swoole_websocket.settings.swoole.swoole9504.options.max_request'),
            'dispatch_mode' => config('swoole_websocket.settings.swoole.swoole9504.options.dispatch_mode'),
            'debug_mode' => config('swoole_websocket.settings.swoole.swoole9504.options.debug_mode'),
            'heartbeat_check_interval' => config('swoole_websocket.settings.swoole.swoole9504.options.heartbeat_check_interval'),
            'heartbeat_idle_time' => config('swoole_websocket.settings.swoole.swoole9504.options.heartbeat_idle_time'),
        ));
        $handler = new ChatSwooleHandler($this->ws);
        $this->ws->on('workerStart', array($this, 'onWorkerStart'));
        $this->ws->on('handshake', array($handler, 'onHandshake'));
        $this->ws->on('message', array($handler, 'onMessage'));
        $this->ws->on('request', array($handler, 'onRequest'));
        $this->ws->on('close', array($handler, 'onClose'));
        //注册事件
        $this->ws->start();
    }

    public function stop()
    {
        $pid = $this->getPid();
        if (!$this->isRunning($pid)) {
            $this->error("Failed! There is no server process running.");
            exit(1);
        }
        $isRunning = $this->killProcess($pid, SIGTERM, 15);
        if ($isRunning) {
            $this->error('Unable to stop the server process.');
            exit(1);
        }
        $this->removePidFile();
        $this->info('swoole server stoped');
    }

    public function reload()
    {
        $pid = $this->getPid();
        if (!$this->isRunning($pid)) {
            $this->error("Failed! There is no server process running.");
            exit(1);
        }
        $isRunning = $this->killProcess($pid, SIGUSR1);
        if (!$isRunning) {
            $this->error('process reload Failed');
            exit(1);
        }
        $this->info('swoole server reloaded');
    }

    public function restart()
    {
        $pid = $this->getPid();
        if ($this->isRunning($pid)) {
            $this->stop();
        }
        $this->start();
    }

    public function infos(){
        $pid = $this->getPid();
        $isRunning = $this->isRunning($pid);
        $host = config('swoole_websocket.settings.swoole.server.host');
        $port = config('swoole_websocket.settings.swoole.server.port');
        $reactorNum = config('swoole_websocket.settings.swoole.swoole9504.options.reactor_num');
        $workerNum = config('swoole_websocket.settings.swoole.swoole9504.options.worker_num');
        $taskWorkerNum = config('swoole_websocket.settings.swoole.swoole9504.options.task_worker_num');
        $logFile = config('swoole_websocket.settings.swoole.swoole9504.options.log_file');
        $dispatchMode = config('swoole_websocket.settings.swoole.swoole9504.options.dispatch_mode');
        $daemonize = config('swoole_websocket.settings.swoole.swoole9504.options.daemonize');
        $table = [
            ['PHP Version', 'Version' => phpversion()],
            ['BulletSwoole Version', 'Version' => swoole_version()],
            ['Laravel Version', $this->getApplication()->getVersion()],
            ['Listen IP', $host],
            ['Listen Port', $port],
            ['Server Status', $isRunning ? 'Online' : 'Offline'],
            ['Reactor Num', $reactorNum],
            ['Worker Num', $workerNum],
            ['Task Worker Num', $taskWorkerNum ],
            ['PID', $isRunning ? $pid : 'None'],
            ['Log Path', $logFile],
            ['Dispatch Mode', $dispatchMode],
            ['Daemonize', $daemonize],
        ];

        $this->table(['Name', 'Value'], $table);
    }


    public function onWorkerStart($server, $worker_id)
    {
        // if ($worker_id == 0) {
            // // 载入框架入口文件
            // $watch = config('swoole_http.watch');
            // $dir = $watch['dir'];
            // $list = $watch['file'];
            // foreach ($dir as $dr) {
            //     foreach (array_diff(scandir($dr), array('.', '..')) as $item) {
            //         $list[] = $dr . '/' . $item;
            //     }
            // }
            // $notify = inotify_init();
            // foreach ($list as $item) {
            //     inotify_add_watch($notify, $item, IN_CREATE | IN_DELETE | IN_MODIFY);
            // }
            // swoole_event_add($notify, function () use ($notify, $server) {
            //     $events = inotify_read($notify);
            //     if (!empty($events)) {
            //         echo "88888\n";
            //         $server->reload();
            //     }
            // });
        // }
    }

    //检测是否加载swoole扩展
    protected function detectSwoole()
    {
        if (strtolower(substr(PHP_OS, 0, 3)) == 'win') {
            $this->error('BulletSwoole extension doesn\'t support Windows OS.');
            exit(1);
        }
        if (!extension_loaded('swoole')) {
            $this->error('The ext-swoole is required! (pecl install swoole)');
            exit(1);
        }
    }

    //swoole是否在运行
    protected function isRunning($pid)
    {
        if (!$pid) {
            return false;
        }
        return \Swoole\Process::kill((int) $pid, 0);
    }

    //获取pid
    protected function getPid()
    {
        if ($this->pid) {
            return $this->pid;
        }
        $pid = null;
        $path = $this->getPidPath();
        if (file_exists($path)) {
            $pid = (int) file_get_contents($path);
            if (!$pid) {
                $this->removePidFile();
            } else {
                $this->pid = $pid;
            }
        }
        return $this->pid;
    }

    //获取pid文件
    protected function getPidPath()
    {
        return config('swoole_websocket.settings.swoole.swoole9504.options.pid_file');
    }

    //删除pid文件
    protected function removePidFile()
    {
        if (file_exists($this->getPidPath())) {
            unlink($this->getPidPath());
        }
    }

    //杀死进程
    protected function killProcess($pid, $sig, $wait = 0)
    {
        \Swoole\Process::kill($pid, $sig); //杀死进程
        if ($wait) {
            $start = time();
            do {
                if (!$this->isRunning($pid)) {
                    break;
                }
                usleep(1000000); //延迟1秒执行
            } while (time() < $start + $wait);
        }
        return $this->isRunning($pid);
    }

}
